package com.twoscreen;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.NotPersistent;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.datastore.Key;

@PersistenceCapable
public class Response {
	@NotPersistent
	private static PersistenceManager pm = PMF.get().getPersistenceManager();
	
	@NotPersistent
	private static final Logger log = Logger.getLogger("com.twoscreen.Response.java");
	
	@NotPersistent
	private static final String replyPropertyName = "reply";
	
	@NotPersistent
	private static final String questionIdPropertyName = "questionId";
	
	@NotPersistent
	private static final String viewerIdPropertyName = "viewerId";

	@NotPersistent
	private static final String timestampPropertyName = "timeStamp";
	
	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Key key; 
	
	@Persistent
	private String reply;
	
	@Persistent
	private long questionId;
	
	@Persistent
	private String viewerId;
	
	@Persistent
	private Date timeStamp;

	public Response(String reply, long questionId, String viewerId) {
		//Automatically generated key so key is left as null
		this.reply = reply;
		this.questionId = questionId;
		this.viewerId = viewerId;
		this.timeStamp = new Date();
		pm.makePersistent(this);
		log.info("Response added: questionId: " + questionId +
				" viewerId: " + viewerId +
				" reply: " + reply);
		
		//Assign points if the response is correct
		assignPoints(questionId, viewerId, reply);
	}
	
	public Key getKey() {
		return key;
	}

	public String getReply() {
		return reply;
	}

	public long getQuestionId() {
		return questionId;
	}

	public String getViewerId() {
		return viewerId;
	}

	public Date getTimeStamp() {
		return timeStamp;
	}

	public static String getReplypropertyname() {
		return replyPropertyName;
	}

	public static String getQuestionIdpropertyname() {
		return questionIdPropertyName;
	}

	public static String getVieweridpropertyname() {
		return viewerIdPropertyName;
	}

	public static String getTimestamppropertyname() {
		return timestampPropertyName;
	}
	
	/**
	 * Get a count of the number of responses received for a given question ID
	 * Useful for generating visualisation and statistics for questions
	 * @param questionId The question ID to perform the count
	 * @return A Map containing a String of the reply and Integer of the count
	 */
	public static Map<String, Integer> getRepliesTallyMap(long questionId) {
		log.info("Getting tally map for question ID: " + questionId);
		Map<String, Integer> replyMap = new HashMap<String, Integer>();
		Query q = pm.newQuery(Response.class);
		q.setFilter(questionIdPropertyName + " == questionIdParam");
		q.declareParameters("String questionIdParam");

		//Create a Map tallying the unique replies
		try {
			@SuppressWarnings("unchecked")
			List<Response> results = (List<Response>) q.execute(questionId);
			for (Response result : results) {
				Boolean exists = true;
				String thisReply = result.getReply();
				int tally = 0;
				
				try {
					tally = replyMap.get(thisReply);
				} catch (NullPointerException e) {
					//The reply does not exist - add it and the count
					replyMap.put(thisReply, 1);
					exists = false;
				}
				
				if (exists) {
					//Increment the tally and add it back to the map
					tally++;
					replyMap.put(thisReply, tally);
				}
			}
		} finally {
			q.closeAll();
		}
		
		return replyMap;
	}
	
	/**
	 * Returns a tally of responses received for a given question ID in a format
	 * readable by the JQBar jQuery visualisation used in app marking the correct
	 * answer in green #33FF99
	 * @param questionId The Question ID to search by
	 * @return A JSON like array for JQBar visualisation
	 */
	public static String getRepliesJQBar(long questionId) {
		log.info("Getting replies in jQuery bar format for question ID: " + questionId);
		StringBuilder sb = new StringBuilder();
		Map<String, Integer> replyMap = new HashMap<String, Integer>();
		replyMap = Response.getRepliesTallyMap(questionId);
		String correct = QuestionMulti.getCorrectAnswer(questionId);
		
		//Build the string
		log.info("Building the string");
		//Add tally data
		for (Map.Entry<String, Integer> entry : replyMap.entrySet()) {
			sb.append("[" + entry.getValue() + ", '" + entry.getKey() + "', ");
			if (entry.getKey().equals(correct)) {
				sb.append("'#33FF99'");
			} else {
				sb.append("'#6699FF'");
			}
			sb.append("],");
		}
		
		//Remove the last comma
		if (sb.length() > 3) {
			sb.deleteCharAt(sb.length()-1);
		}
		
		return sb.toString();
	}
	
	/**
	 * Assigns the corresponding points to a viewer if their response is correct
	 * @param questionId The question ID being responded to
	 * @param viewerId The viewer ID submitting the response
	 * @param reply	The response being submitted
	 */
	private static void assignPoints(long questionId, String viewerId, String reply) {
		log.info("Assigning points");
		//Get the correct answer
		QuestionMulti qm = QuestionMulti.getQMObject(questionId);
		String correct = qm.getCorrect();
		int points = qm.getPoints();
		
		//If the reply is correct add the points
		if (reply.equals(correct)) {
			new Points(viewerId, questionId, points);
		}
	}
	
	//Response tests do not work as they rely on a matching Question.
	
//	/**
//	 * Runs all the tests on the class
//	 * @return Test results JSON
//	 */
//	public static JSONObject test() {
//		Test test = new Test("Response");
//		test.addMethod(testConstructor());
//		test.addMethod(testGetRepliesTallyMap());
//		test.addMethod(testGetRepliesJQBar());
//		return test.getJSONObject();
//	}
//	
//	/**
//	 * Tests the constructor method
//	 * @return Test results JSON
//	 */
//	private static JSONObject testConstructor() {
//		TestMethod tm = new TestMethod("testConstructor");
//		
//		//Test base
//		String reply = "abcd";
//		String viewerId = "efgh";
//		int questionId = 12;
//		Response test = new Response(reply, questionId, viewerId);
//    	
//		//Test 1
//		Boolean test1 = false;
//    	String testStack1 = "";
//    	try {
//    		Response inTest = pm.getObjectById(Response.class, questionId);
//    		test1 = (inTest != null);
//    	} catch (Exception e) {
//    		testStack1 = e.getMessage();
//    	}
//		tm.addTest("Entry exists", test1, testStack1);
//		
//		//Remove the test object
//		pm.deletePersistent(test);
//		
//    	return tm.getJSONObject();
//    }
//	
//	/**
//	 * Tests the getRepliesTallyMap method
//	 * @return Test results JSON
//	 */
//	private static JSONObject testGetRepliesTallyMap() {
//		TestMethod tm = new TestMethod("testGetRepliesTallyMap");
//		
//		//Test base
//		String reply = "abcd";
//		String viewerId = "efgh";
//		int questionId = 12;
//		Response test = new Response(reply, questionId, viewerId);
//		String reply2 = "ijkl";
//		String viewerId2 = "mnop";
//		int questionId2 = 12;
//		Response test2 = new Response(reply2, questionId2, viewerId2);
//    	
//		//Test 1
//		Boolean test1 = false;
//    	String testStack1 = "";
//    	try {
//    		Map<String, Integer> inTest = getRepliesTallyMap(questionId2);
//    		test1 = (inTest.size() == 2);
//    	} catch (Exception e) {
//    		testStack1 = e.getMessage();
//    	}
//		tm.addTest("Count correct", test1, testStack1);
//		
//		//Remove the test object
//		pm.deletePersistent(test);
//		pm.deletePersistent(test2);
//		
//    	return tm.getJSONObject();
//    }
//	
//	/**
//	 * Tests the getRepliesJQBar method
//	 * @return Test results JSON
//	 */
//	private static JSONObject testGetRepliesJQBar() {
//		TestMethod tm = new TestMethod("testGetRepliesJQBar");
//		
//		//Test base
//		String reply = "abcd";
//		String viewerId = "efgh";
//		int questionId = 12;
//		Response test = new Response(reply, questionId, viewerId);
//		String reply2 = "ijkl";
//		String viewerId2 = "mnop";
//		int questionId2 = 12;
//		Response test2 = new Response(reply2, questionId2, viewerId2);
//    	
//		//Test 1
//		Boolean test1 = false;
//    	String testStack1 = "";
//    	try {
//    		String inTest = getRepliesJQBar(questionId2);
//    		test1 = (inTest.length() > 30);
//    	} catch (Exception e) {
//    		testStack1 = e.getMessage();
//    	}
//		tm.addTest("Length correct", test1, testStack1);
//		
//		//Remove the test object
//		pm.deletePersistent(test);
//		pm.deletePersistent(test2);
//		
//    	return tm.getJSONObject();
//    }
	

}
